package com.sl.proxy.server.base.proxy;

import cn.hutool.extra.spring.SpringUtil;
import com.sl.proxy.core.ProxyDataTypeEnum;
import com.sl.proxy.core.ProxyMessage;
import com.sl.proxy.core.ProxyMessageHandler;
import com.sl.proxy.core.dispatcher.DefaultDispatcher;
import com.sl.proxy.core.dispatcher.Dispatcher;
import com.sl.proxy.server.proxy.core.BytesMetricsHandler;
import com.sl.proxy.server.proxy.core.TcpVisitorChannelHandler;
import com.sl.proxy.server.proxy.core.UdpVisitorChannelHandler;
import com.sl.proxy.server.proxy.handler.ProxyServerMessageHandler;
import com.sl.proxy.server.proxy.security.TcpVisitorSecurityChannelHandler;
import com.sl.proxy.server.proxy.security.UdpVisitorSecurityChannelHandler;
import com.sl.proxy.server.proxy.security.VisitorFlowLimiterChannelHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

/**
 * 代理配置
 *
 * @author: aoshiguchen
 * @date: 2022/10/8
 */
@Configuration
public class ProxyConfiguration {

    @Bean("serverDispatcher")
    public Dispatcher<ChannelHandlerContext, ProxyMessage> serverDispatcher() {
        Map<String, ProxyServerMessageHandler> beansOfType = SpringUtil.getBeansOfType(ProxyServerMessageHandler.class);
        List<ProxyServerMessageHandler> list = beansOfType.values().stream().toList();
        Dispatcher<ChannelHandlerContext, ProxyMessage> dispatcher = new DefaultDispatcher<>("MessageDispatcher", list,
                proxyMessage -> ProxyDataTypeEnum.of((int) proxyMessage.getType()) == null ?
                        null : ProxyDataTypeEnum.of((int) proxyMessage.getType()).getName());
        return dispatcher;
    }


    @Bean("tcpServerBossGroup")
    public NioEventLoopGroup tcpServerBossGroup(ProxyConfig proxyConfig) {
        ProxyConfig.Server server = proxyConfig.getServer();
        ProxyConfig.Tcp tcp = server.getTcp();
        return new NioEventLoopGroup(tcp.getBossThreadCount());
    }

    @Bean("tcpServerWorkerGroup")
    public NioEventLoopGroup tcpServerWorkerGroup(ProxyConfig proxyConfig) {
        return new NioEventLoopGroup(proxyConfig.getServer().getTcp().getWorkThreadCount());
    }

    @Bean("tcpServerBootstrap")
    public ServerBootstrap tcpServerBootstrap(@Qualifier("tcpServerBossGroup") NioEventLoopGroup tcpServerBossGroup,
                                              @Qualifier("tcpServerWorkerGroup") NioEventLoopGroup tcpServerWorkerGroup,
                                              ProxyConfig proxyConfig
    ) {
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(tcpServerBossGroup, tcpServerWorkerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        if (null != proxyConfig.getServer().getTcp().getTransferLogEnable() && proxyConfig.getServer().getTcp().getTransferLogEnable()) {
                            ch.pipeline().addFirst(new LoggingHandler(TcpVisitorChannelHandler.class));
                        }
                        ch.pipeline().addFirst(new BytesMetricsHandler());
//            ch.pipeline().addLast(new ChannelTrafficShapingHandler(1024 * 1024 * 20, 1024 * 1024 * 20, 100, 20000));
                        ch.pipeline().addLast(new TcpVisitorSecurityChannelHandler());
                        ch.pipeline().addLast("flowLimiter", new VisitorFlowLimiterChannelHandler());
                        ch.pipeline().addLast(new TcpVisitorChannelHandler());
                    }
                });
        return bootstrap;
    }

    @Bean("udpServerBossGroup")
    public NioEventLoopGroup udpServerBossGroup(ProxyConfig proxyConfig) {
        return new NioEventLoopGroup(proxyConfig.getServer().getUdp().getBossThreadCount());
    }

    @Bean("udpServerWorkerGroup")
    public NioEventLoopGroup udpServerWorkerGroup(ProxyConfig proxyConfig) {
        return new NioEventLoopGroup(proxyConfig.getServer().getUdp().getWorkThreadCount());
    }

    @Bean("udpServerBootstrap")
    public Bootstrap udpServerBootstrap(@Qualifier("udpServerBossGroup") NioEventLoopGroup udpServerBossGroup,
                                        @Qualifier("udpServerWorkerGroup") NioEventLoopGroup udpServerWorkerGroup,
                                        ProxyConfig proxyConfig) {
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(udpServerBossGroup)
                // 主线程处理
                .channel(NioDatagramChannel.class)
                // 广播
                .option(ChannelOption.SO_BROADCAST, true)
                // 设置读缓冲区为2M
                .option(ChannelOption.SO_RCVBUF, 2048 * 1024)
                // 设置写缓冲区为1M
                .option(ChannelOption.SO_SNDBUF, 1024 * 1024)
                .handler(new ChannelInitializer<NioDatagramChannel>() {
                    @Override
                    protected void initChannel(NioDatagramChannel ch) {
                        ChannelPipeline pipeline = ch.pipeline();
                        if (null != proxyConfig.getServer().getUdp().getTransferLogEnable() && proxyConfig.getServer().getUdp().getTransferLogEnable()) {
                            ch.pipeline().addFirst(new LoggingHandler(UdpVisitorChannelHandler.class));
                        }
                        pipeline.addLast(udpServerWorkerGroup, new UdpVisitorSecurityChannelHandler());
                        ch.pipeline().addLast("flowLimiter", new VisitorFlowLimiterChannelHandler());
                        pipeline.addLast(udpServerWorkerGroup, new UdpVisitorChannelHandler());
                    }
                });
        return bootstrap;
    }

    @Bean("tunnelBossGroup")
    public NioEventLoopGroup tunnelBossGroup(ProxyConfig proxyConfig) {
        return new NioEventLoopGroup(proxyConfig.getTunnel().getBossThreadCount());
    }

    @Bean("tunnelWorkerGroup")
    public NioEventLoopGroup tunnelWorkerGroup(ProxyConfig proxyConfig) {
        return new NioEventLoopGroup(proxyConfig.getTunnel().getWorkThreadCount());
    }


//    @Bean
//    public NeutrinoCoreRuntimeNativeRegistrar neutrinoCoreRuntimeNativeRegistrar() {
//        return new NeutrinoCoreRuntimeNativeRegistrar();
//    }

}
